<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    
    <script>
      function defineReactive(obj, key, val) {
        /* 一个Dep类对象 */
        const dep = new Dep();
        Object.defineProperty(obj, key, {
          enumerable: true, //属性可枚举
          configurable: true, //属性可被修改或删除
          get: function reactiveGetter() {
            /* 将Dep.target（即当前的Watcher对象存入dep的subs中） */
            dep.addSub(Dep.target); //这里进行依赖收集,其实依赖收集的过程就是把watcher实例存放到对应的Dep对象中去，
            return val;
          }, //get方法可以让当前的Watcher对象（Dep.target）存放到它的subs中（addSub）方法
          set: function reactiveSetter(newVal) {
            if (newVal === val) return;
            val = newVal;
            /* 在set的时候触发dep的notify来通知所有的Watcher对象更新视图 */
            dep.notify();
          },
        });
      }
      function cb(val) {
        console.log("视图更新啦~", val);
      }

      function observer(value) {//Observer是一个类,它的作用是给对象属性添加getter和setter,用于 依赖收集 和 派发更新
        if (!value || typeof value !== "object") {
          return;
        }
        Object.keys(value).forEach((key) => {
          defineReactive(value, key, value[key]);
        });
      }

      class Vue {
        constructor(option) {
          this._data = option.data;
          observer(this._data);
          /* 新建一个Watcher观察者对象，这时候Dep.target会指向这个Watcher对象 */
          new Watcher();
          /* 在这里模拟render的过程，为了触发test属性的get函数 */
          console.log("render~", this._data);
        }
      }

      class Dep {
        //订阅者
        constructor() {
          //用来存放watcher对象的数组
          this.subs = [];
        }
        addSub(sub) {
          this.subs.push(sub);
        }
        notify() {
          //通知所有的watcher对象更新视图
          this.subs.forEach((sub) => {
            sub.update();
          });
        }
      }

      class Watcher {
        constructor() {
          Dep.target = this;
        }
        update() {
          console.log("视图更新啦~");
        }
      }
      Dep.target = null;

      let o = new Vue({
        template: `<div>
            <span>{{text1}}</span> 
            <span>{{text2}}</span> 
        <div>`,
        data: {
          text1: "text1",
          text2: "text2",
          text3: "text3",
        },
      });
      o._data.text1 = "hello,world."; /* render~ */
    </script>
  </body>
</html>
